
package cell

import (
	"github.com/treelab/updatecolumn/models"
)

  func instanceType() int32 {
    return models.Lookup
  }

  //#region cell value convert
  cellValueToString({ cell }: { cell: Cell }): string {
    const lookup = cell.hasLookupCell.get();
    return _.join(
      lookup.map(c => this.getVisibleTextByCell(c)).filter(c => c),
      ',',
    );
  }

  cellValueToNumber({ cell }: IColumnAndCell) {
    const lookup = cell.hasLookupCell.get();
    let result = 0;
    if (lookup.length) {
      result = parseFloat(
        _.join(
          lookup.map((c: any) => this.getVisibleTextByCell(c)),
          '',
        ).replace(/[^0-9.-]/g, ''),
      );
    }
    return isNaN(result) ? 0 : result;
  }

  cellValueToRating({ cell }: IColumnAndCell) {
    const lookup = cell.hasLookupCell.get();
    let finalRating = 0;
    if (lookup.length) {
      const lookupNumber = lookup.map((c: any) => this.getVisibleTextByCell(c));
      if (!_.isNaN(Number(lookupNumber[0]))) {
        finalRating = Math.round(Number(lookupNumber[0]));
      }
    }
    return finalRating;
  }

  cellValueToDatetime({ cell }: IColumnAndCell) {
    const lookup = cell.hasLookupCell.get();
    if (lookup && lookup.length) {
      const lookupText = lookup.map(c => this.getVisibleTextByCell(c));
      return lookupText[0].trim();
    }
    return '';
  }

  cellValueToCheckbox({ cell }: IColumnAndCell) {
    const lookup = cell.hasLookupCell.get();
    if (lookup && lookup.length) {
      const number = lookup.map(c => this.getVisibleTextByCell(c));
      if (number.length) {
        return number[0] === 'true' || number[0] === 'checked';
      }
    }
    return false;
  }

  cellValueToSelect({ column, cell }: IColumnAndCell) {
    const lookup = cell.hasLookupCell.get();
    if (lookup.length) {
      const lookupToSelect = lookup.map((c: any) => this.getVisibleTextByCell(c));
      const option = _.find(column.hasColumnOption.get(), i => _.trim(i.name) === _.trim(lookupToSelect[0]));
      return option;
    }
  }
  cellValueToMultiSelect({ cell, column }: IColumnAndCell) {
    const lookup = cell.hasLookupCell.get();
    if (lookup.length) {
      const lookupToSelect = lookup.map((c: any) => this.getVisibleTextByCell(c));
      const options = _.filter(column.hasColumnOption.get(), i => _.trim(i.name) === _.trim(lookupToSelect[0]));
      return options;
    }
    return [];
  }
  //#endregion

  setCellValue({ cell, action }: ICCAction, o: CommonOptions) {
    const { delQuads } = o;
    const { value, type: actionType } = action;
    const { foreignCells } = value as LookupCellValue;
    // TODO: need to benchmark this, because the type function will return more nodes.
    // can optimize by using $columnId as initial function
    if (actionType === UpdateAction.ADD_VALUE || actionType === UpdateAction.SET_VALUE) {
      // TODO: Remove type preciate once we add in type function in validation query.
      // if (cell.uid && actionType === UpdateAction.SET_VALUE && !isNewCell) {
      //   delQuads.push(...this.dgraphHelperService.getRemoveEdgeQuads(cell.uid, Relation.HAS_LOOKUP_CELL));
      // }

      const existingLookupCells = cell.hasLookupCell.get();
      foreignCells.forEach(foreignCell => {
        // if foreignCell is already a foreigCell looked up by cell, do not recreate it again
        if (!existingLookupCells.find(({ id }: any) => id === foreignCell.foreignLookupCellId)) {
          this.createForeignLookupCell({ cell, foreignCell }, o);
        }
      });
    }
    if (actionType === UpdateAction.REMOVE_VALUE) {
      if (!cell.uid) {
        throw new Error('cannot remove value from cell');
      }
      _.forEach(
        foreignCells.filter(foreignCell => foreignCell.foreignLookupCellId),
        foreignCell =>
          delQuads.push(
            ...this.dgraphHelperService.getRemoveEdgeQuads(
              cell.uid!,
              Relation.HAS_LOOKUP_CELL,
              foreignCell.foreignLookupCellId,
            ),
          ),
      );
    }
    return this;
  }

  getCellValue(cell: Cell | any) {
    return cell.lookup;
  }

  async convertCell(
    command: UpdateColumn_v2,
    o: CommonOptions,
    { columnCopy, referenceColumns }: { columnCopy: Column; referenceColumns?: Column[] },
  ) {
    const { tableId, column, columnConfig } = command;
    const { columnType: fromType } = columnCopy;
    const { foreignLookupColumnId, recordReferenceColumnId } = columnConfig as LookupColumnConfig;
    const cells = column.hasCell.get().reduce((obj: { [key: string]: Cell }, val) => {
      obj[val.rowId] = val;
      return obj;
    }, {});

    if (this.needEmptyCell(command)) {
      const fromCellHandle = this.instanceFactory.getCellInstance(fromType as ColumnType);
      fromCellHandle.emptyCells(command, column.hasCell.get(), false, o);
    }

    const rows = await this.rowReadService.getForeignRows_v2({
      tableId,
      columnId: recordReferenceColumnId,
      getForeignColumn: true,
      transaction: o.txn,
    });
    const filteredRows = rows.filter((row: any) => {
      const _cells = row.hasCell.get();
      return _cells.length && _cells[0].hasForeignRow.get().length;
    });
    const visableNameMapping = new Map<string, string>();
    _.forEach(filteredRows, row => {
      const foreignCells: ForeignCellValue[] = [];
      const cell = row.hasCell.get()[0];
      const cellForeignRows = cell.hasForeignRow.get();

      _.forEach(cellForeignRows, cellForeignRow => {
        // If the foreign lookup cell exists update it, if it doesn't create a temporary one with assignment.
        const foreignLookupCell = cellForeignRow.hasCell.get().find((c: any) => c.columnId === foreignLookupColumnId);
        if (foreignLookupCell) {
          foreignCells.push({
            foreignLookupColumnId,
            foreignLookupRowId: cellForeignRow.uid!,
            foreignLookupCellId: foreignLookupCell.uid!,
          });
        } else {
          foreignCells.push({
            foreignLookupColumnId,
            foreignLookupRowId: cellForeignRow.uid!,
          });
        }
      });
      if (foreignCells.length) {
        const action = {
          type: UpdateAction.SET_VALUE,
          value: {
            type: ColumnType.LOOKUP,
            foreignCells,
          },
        };
        const rowData = this.upsertCell(cells[row.uid!], { command: { ...command, rowId: row.uid, action } }, o);
        if (rowData) {
          visableNameMapping.set(rowData.uid, rowData.visibleName);
        }
      }
    });
    this.updateReferenceColumnAndDependencies({
      column,
      visableNameMapping,
      o,
      _metadata: command._metadata,
      referenceColumns,
    });
    return this;
  }

  emptyCells(command: UpdateColumn_v2, cells: readonly Cell[], deleteCells: boolean, o: CommonOptions) {
    if (!deleteCells) {
      _.forEach(cells, cell => {
        o.delQuads.push(...this.dgraphHelperService.getRemoveEdgeQuads(cell.uid!, Relation.HAS_LOOKUP_CELL));
      });
    }
    super.emptyCells(command, cells, deleteCells, o);
    return this;
  }

   createForeignLookupCell(
    { cell, foreignCell }: { cell: Cell; foreignCell: ForeignCellValue },
    o: CommonOptions,
  ) {
    const isNewCell = !foreignCell?.foreignLookupCellId;
    const _cell = isNewCell
      ? o.ormTransaction.nodeFor(Cell)
      : o.ormTransaction.nodeFor(Cell, { uid: foreignCell.foreignLookupCellId });
    cell.hasLookupCell.add(_cell);

    if (isNewCell) {
      _cell.type = GraphNodeTypes.CELL;
      _cell.columnId = foreignCell.foreignLookupColumnId;
      _cell.rowId = foreignCell.foreignLookupRowId;

      const foreignColumn = o!.ormTransaction.nodeFor(Column, { uid: foreignCell.foreignLookupColumnId });
      // foreignColumn.uid = foreignCell.foreignLookupColumnId;
      foreignColumn.hasCell.add(_cell);
      _cell.fromColumn.add(foreignColumn);

      // @TODO rowid 获取不到
      // @TODO 这个还需要测试
      if (!_cell.rowId) {
        throw new Error(`Could not find  ${_cell} rowId in lookup cell`);
      }
      const foreignRow = o.ormTransaction.nodeFor(Row, { uid: _cell.rowId });
      foreignRow.hasCell.add(_cell);
      // o.setQuads.push(
      //   `<${foreignCell.foreignLookupRowId}> <${Relation.HAS_CELL}> ${this.getNodeId(this.getNodeTempId(_cell))} .`,
      // )
    }
  }

